import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_menu/biz/order/detail/detail_controller.dart';
import 'package:flutter_menu/data/provider/dept_provider.dart';
import 'package:flutter_menu/data/provider/formula_provider.dart';
import 'package:flutter_menu/data/provider/material_dept_stock_provider.dart';
import 'package:flutter_menu/data/provider/material_provider.dart';
import 'package:flutter_menu/data/provider/option_material_provider.dart';
import 'package:flutter_menu/data/provider/product_provider.dart';
import 'package:flutter_menu/data/provider/sellout_provider.dart';
import 'package:flutter_menu/data/provider/shop_provider.dart';
import 'package:flutter_menu/model/material_model.dart';
import 'package:flutter_menu/model/meal_spec_model.dart';
import 'package:flutter_menu/model/product_model.dart';
import 'package:flutter_menu/model/shope_model.dart';
import 'package:get/get.dart';

const double MAX_VALUE =
    double.infinity; //菜品最大沽清库存,此值代表着无限可售 以前用Int.MAX_VALUE 后来改为 double了

class SellHelper {
  ProductProvider productProvider = Get.find<ProductProvider>();
  SelloutProvider selloutProvider = Get.find<SelloutProvider>();
  MaterialProvider materialProvider = Get.find<MaterialProvider>();
  FormulaProvider formulaProvider = Get.find<FormulaProvider>();
  MaterialDeptStockProvider materialDeptStockProvider =
      Get.find<MaterialDeptStockProvider>();
  OptionMaterialProvider optionMaterialProvider =
      Get.find<OptionMaterialProvider>();
  ShopProvider shopProvider = Get.find<ShopProvider>();
  DeptProvider deptProvider = Get.find<DeptProvider>();

  Future<double> getSpecNumberForSale(String productId, Spec specs) async {
    double num = double.infinity;

    // 获取产品的沽清状态
    var productSelloutStatus = await getProductSelloutStatus(specs);
    switch (productSelloutStatus) {
      case ProductSelloutStatus.NONE:
        num = await getSpecNumberIfNone(productId, specs);
        break;

      case ProductSelloutStatus.MATERIAL:
        num = await getSpecNumberIfMaterialSellout(specs);
        break;

      case ProductSelloutStatus.OPTION:
      case ProductSelloutStatus.OPTION_MATERIAL:
        num = await getSpecNumberIfOptionSellout(specs);
        break;

      case ProductSelloutStatus.SPEC:
      case ProductSelloutStatus.SPEC_MATERIAL:
        num = await getSpecNumberIfSpecSellout(specs);
        break;

      case ProductSelloutStatus.SPEC_OPTION:
      case ProductSelloutStatus.SPEC_OPTION_MATERIAL:
        double optionGroupMin = await getSpecNumberIfOptionSellout(specs);
        if (optionGroupMin == double.infinity) {
          // 如果规格内没有子选项组完全沽清，取规格的沽清数量
          num = await getSpecNumberIfSpecSellout(specs);
        } else {
          // 否则取最小选项组的数量和规格的沽清后的数量的最小值
          var count = await getSpecNumberIfSpecSellout(specs);
          num = optionGroupMin < count ? optionGroupMin : count;
        }
        break;
    }

    return num;
  }

  /// 如果规格没有沽清状态
  /// @return
  Future<double> getSpecNumberIfNone(String productId, Spec specs,
      {int selectCount = 1}) async {
    //菜品数量关联了原料
    if (shopProvider.isEnableMaterialLinkProduct()) {
      return await count(productId, specs.id, selectCount: selectCount);
    } else {
      return MAX_VALUE;
    }
  }

  /// 如果只有原料沽清了
  ///  规格数量= 各种原料的沽清数量 / 菜品用料配方 的最小值
  ///  菜品用料配方 = 规格用料配方 + 所有选项组默认用料配方
  Future<double> getSpecNumberIfMaterialSellout(Spec specs,
      {int selectCount = 1}) async {
    // 获取规格默认的选中的选项和规格本身用到的所有材料
    var specDefaultAllMaterial = await getSpecDefaultAllMaterial(specs);

    // 获取规格内已沽清的原料R
    if (specDefaultAllMaterial.isNotEmpty) {
      for (var materialDto in specDefaultAllMaterial) {
        var selloutDetails =
            await selloutProvider.getSelloutDetails(materialDto.materialId!, 3);
        if (selloutDetails?.isSellout == 1) {
          // 如果有直接沽清的原料，说明菜品已经售罄，不可买
          if (selloutDetails?.surplus == 0.0) {
            return 0.0;
          }
        }
      }
    }

    // 获取菜品配方原料使用情况
    var materialUsage =
        await getSpecDefaultFormulaUsage(specs, true, selectCount: selectCount);

    // 各原料可制作数量的集合
    var quantityList = <double>[];

    if (materialUsage.isEmpty) return double.infinity;

    for (var key in materialUsage.keys) {
      for (var materialDto in specDefaultAllMaterial) {
        if (key == materialDto.materialId) {
          var quantity = double.infinity;
          var selloutDetails = await selloutProvider.getSelloutDetails(
              materialDto.materialId!, 3);
          if (materialUsage[key] != 0.0 && selloutDetails?.isSellout == 1) {
            quantity = ((selloutDetails?.surplus ?? 0.0) / materialUsage[key]!);
          }
          quantityList.add(quantity);
          break;
        }
      }
    }

    // 返回集合中可制作数量最小的数字
    if (quantityList.isEmpty) return double.infinity;
    return quantityList.reduce((a, b) => a < b ? a : b);
  }

  Future<double> getSpecNumberIfOptionSellout(Spec specs) async {
    double min = double.infinity;

    outer:
    for (var options in specs.options) {
      // 只考虑选项组
      if (options.type == 1) {
        int selloutCount = 0; // 沽清的选项个数
        double sum = 0.0;

        for (var option in options.options) {
          var selloutDetails =
              await selloutProvider.getSelloutDetails(option.id!, 2);
          if (selloutDetails?.isSellout == 1) {
            selloutCount++;
            sum += (selloutDetails?.surplus ?? 0.0).toInt();
          } else {
            // 含有未沽清的,直接跳过
            continue outer;
          }
        }

        // 全部设置沽清了
        if (selloutCount == options.options.length) {
          if (min > sum) min = sum;
        }
      }
    }

    return min;
  }

  /**
   * 获取规格数量,如果规格沽清了 选项没沽清
   */
  Future<double> getSpecNumberIfSpecSellout(Spec specs) async {
    var selloutModel = await selloutProvider.getSelloutDetails(specs.id!, 1);
    return selloutModel?.surplus ?? 0.0;
  }

  /// 获取(点菜时)菜品的沽清状态
  Future<int> getProductSelloutStatus(Spec spec) async {
    int status = ProductSelloutStatus.NONE;
    var selloutDetail = await selloutProvider.getSelloutDetails(spec.id!, 1);
    if (selloutDetail?.isSellout == 1) {
      status |= ProductSelloutStatus.SPEC;
    }
    var options = spec.options;
    if (options != null && options.isNotEmpty) {
      outer:
      for (var option in options) {
        if (option.type != 0 &&
            option.options != null &&
            option.options.isNotEmpty) {
          for (var op in option.options) {
            var opId = op.id ?? '';
            if ((await selloutProvider.getSelloutDetails(opId, 2))?.isSellout ==
                1) {
              status |= ProductSelloutStatus.OPTION;
              break outer;
            }
          }
        }
      }
    }
    var allMaterial = await getSpecsDefaultMaterials(spec);
    if (allMaterial != null && allMaterial.isNotEmpty) {
      for (var materialDto in allMaterial) {
        var materialId = materialDto.materialId;
        if (materialId != null) {
          var isSellout =
              (await selloutProvider.getSelloutDetails(materialId, 3))
                  ?.isSellout;
          if (isSellout == 1) {
            status |= ProductSelloutStatus.MATERIAL;
            break;
          }
        }
      }
    }
    return status;
  }

  Future<List<MaterialModel>> getSpecsDefaultMaterials(Spec specs) {
    return getSpecsDefaultMaterialsWithFilter(specs, true);
  }

  /// 根据规格获取菜品原料
  /// @return
  Future<List<MaterialModel>> getSpecsDefaultMaterialsWithFilter(
      Spec specs, bool filter) async {
    List<MaterialModel> list = [];

    // 获取配方使用情况
    var specFormulaUsage =
        await getFormulaUsage(ItemId(specId: specs.id));
    if (specFormulaUsage.isNotEmpty) {
      for (var key in specFormulaUsage.keys) {
        var material = await materialProvider.getMaterialModelWithIdAsync(key);
        if (material != null) {
          list.add(material);
        }
      }
    }

    // 获取所有默认选项
    var allDefaultOptions = await getAllDefaultOptions(specs, filter);
    if (allDefaultOptions.isNotEmpty) {
      for (var defaultOption in allDefaultOptions) {
        var optionFormulaUsage = await getFormulaUsage(
            ItemId(specId: specs.id, optionId: defaultOption.id));
        if (optionFormulaUsage.isNotEmpty) {
          for (var key in optionFormulaUsage.keys) {
            var material =
                await materialProvider.getMaterialModelWithIdAsync(key);
            if (material != null && !list.contains(material)) {
              list.add(material);
            }
          }
        }
      }
    }

    return list;
  }

  /// 根据原料数量计算当前菜品剩余数量
  Future<double> count(String productId, String? specsId,
      {int selectCount = 1}) async {
    // 当前菜品可做份数
    double count = 0.0;
    var specs = getSpecFromProductId(productId, specsId);
    if (specs == null) return count;

    // 规格和默认选项中菜品的原料用量
    var materialUsage = await getSpecDefaultFormulaUsage(specs, false,
        selectCount: selectCount);

    // 待盘点原料已使用原料量
    var waitMaterialUsage = <String, double>{};
    var product = getProductDto(productId);

    if (materialUsage != null && materialUsage.isNotEmpty) {
      for (var key in materialUsage.keys) {
        // 当前原料待盘点数据
        // var currentWaitMaterial = waitRawInventoryModel.getMaterialByIdAscByUpdateTime(key, product.deptIdOrDefaultId);

        // 当前待盘点原料数量
        /*var currentWaitMaterialCount = currentWaitMaterial?.map((dto) {
          return (dto?.compatCount ?? 0.0) * (dto?.usageAmount ?? 0.0);
        }).reduce((a, b) => a + b) ?? 0.0;*/

        var currentWaitMaterialCount = 0.0;

        waitMaterialUsage[key] = currentWaitMaterialCount;
      }
    }

    // 默认选中的选项的原料使用情况
    var allDefaultOptions = await getAllDefaultOptions(specs, false);
    if (allDefaultOptions.isNotEmpty) {
      for (var defaultOption in allDefaultOptions) {
        await initOptionUsage(specs, defaultOption, materialUsage);
      }
    }

    count = MAX_VALUE;
    if (materialUsage == null || materialUsage.isEmpty) {
      return count; // 没有配方使用情况,说明此道菜不用原料 所有可售数量不受限制
    }

    //TODO getDepartmentOrDefault方法缺失
    var department = deptProvider.loadByIdOrDefault(product?.id);
    //可用数量map = 库存 - 待盘点
    var usableCountMap = waitMaterialUsage.map((key, value) {
      var material = materialProvider.getMaterialModelWithId(key);
      var useNumber = materialDeptStockProvider.getQuantityInStock(
          material?.materialId ?? '', department?.id ?? '');
      return MapEntry(key, useNumber - value);
    });

    // 数量 = 可用原料 / 每份菜品原料用量 的最小数
    count = materialUsage.keys.map((key) {
      if (materialUsage[key] == 0.0) {
        // 防止原料出现单份用量为0 除0出现崩溃
        return MAX_VALUE;
      } else {
        return usableCountMap[key]! / materialUsage[key]!;
      }
    }).reduce((value, element) => value < element ? value : element);

    if (count == MAX_VALUE) return count;

    return count;
  }

  ProductModel? getProductDto(String productId) {
    return productProvider.getProductById(productId);
  }

  Spec? getSpecFromProductId(String? productId, String? specsId) {
    if (productId != null && specsId != null) {
      var productDto = getProductDto(productId);
      if (productDto != null) {
        for (var spec in productDto.specs) {
          if (spec.id == specsId) {
            return spec;
          }
        }
      }
    }
    return null;
  }

  ///获取规格需要消耗的原料包括选中的选项消耗的原料 是否过滤全部沽清的但默认选中的选项
  Future<Map<String, double>> getSpecDefaultFormulaUsage(Spec spec, bool filter,
      {int selectCount = 1}) async {
    Map<String, double> materialUsage = {};
    String? specId = spec.id;
    if (specId != null) {
      var specsUsage = formulaProvider
          .getFormulaModelWithSpecId(specId)
          ?.formulaMaterials
          .asMap()
          .map((index, material) =>
              MapEntry(material.materialId!, material.usageAmount ?? 0));
      if (specsUsage != null) {
        materialUsage.addAll(specsUsage);
      }

      var allDefaultOptions = await getAllDefaultOptions(spec, filter);
      for (var option in allDefaultOptions) {
        var optionId = option.id;
        if (optionId != null) {
          var formulaModelWithOptionId = await optionMaterialProvider
              .getFormulaModelWithOptionId(specId, optionId);
          if (formulaModelWithOptionId != null) {
            var optionUsage = formulaModelWithOptionId.formulaMaterials
                .asMap()
                .map((index, material) =>
                    MapEntry(material.materialId!, material.usageAmount ?? 0));

            if (optionUsage.isNotEmpty) {
              for (var key in optionUsage.keys) {
                materialUsage[key] = (materialUsage[key] ?? 0.0) +
                    (optionUsage[key] ?? 0.0) * selectCount;
              }
            }
          }
        }
      }
    }

    return materialUsage;
  }

  //初始化选项的使用情况 填充materialUsage waitMaterialUsage
  Future<void> initOptionUsage(Spec specs, SpecOption defaultOption,
      Map<String, double> materialUsage) async {
    var formulaUsage = await getFormulaUsage(ItemId(specId: specs.id, optionId: defaultOption.id));

    if (formulaUsage != null && formulaUsage.isNotEmpty) {
      for (var key in formulaUsage.keys) {
        materialUsage[key] =
            (materialUsage[key] ?? 0.0) + (formulaUsage[key] ?? 0.0);
      }
    }
  }

  Spec? getSpecFromSpecList(List<Spec>? specs, String specsId) {
    if (specs != null) {
      for (var spec in specs) {
        if (spec.id == specsId) {
          return spec;
        }
      }
    }
    return null;
  }

  Future<Map<String, double?>> getFormulaUsage(ItemId itemId) async {
    var specId = itemId.specId;
    var optionId = itemId.optionId;

    var usageMap = <String, double?>{};
    if (optionId != null && specId != null) {
      // 加载选项配方表
      var formulaModelWithOptionId =
      await optionMaterialProvider.getFormulaModelWithOptionId(specId, optionId);
      var formulaMaterials = formulaModelWithOptionId?.formulaMaterials;
      if (formulaMaterials != null) {
        usageMap = {
          for (var i in formulaMaterials) i.materialId!: i.usageAmount
        };
      }
    } else if (specId != null) {
      // 加载配方表
      var formulaModelWithSpecId =
      formulaProvider.getFormulaModelWithSpecId(specId);
      var formulaMaterials = formulaModelWithSpecId?.formulaMaterials;
      if (formulaMaterials != null) {
        usageMap = {
          for (var i in formulaMaterials) i.materialId!: i.usageAmount
        };
      }
    }
    return usageMap;
  }

  /// 获取规格的默认配方用料 =  规格用料配方 + 所有选项组默认用料配方
  /// @param specs
  /// @return
  Future<List<MaterialModel>> getSpecDefaultAllMaterial(Spec specs) async {
    var list = <MaterialModel>[];

    // 获取规格菜品配方里的原料
    var specDefaultFormulaUsage =
        await getSpecDefaultFormulaUsage(specs, false);

    if (specDefaultFormulaUsage != null && specDefaultFormulaUsage.isNotEmpty) {
      for (var key in specDefaultFormulaUsage.keys) {
        var material = materialProvider.getMaterialModelWithId(key);
        if (material != null) {
          list.add(material);
        }
      }
    }

    return list;
  }

  ///获取规格内所有默认选中的选项 filter 是否过滤掉已经沽清数量为0的 默认选中的非选项组的选项
  Future<List<SpecOption>> getAllDefaultOptions(Spec specs, bool filter) async {
    List<SpecOption> all = [];
    List<SpecOption> option = specs.options;

    if (option.isNotEmpty) {
      for (var option1 in option) {
        if (option1 == null) continue;

        if (option1.type == 0) {
          if (option1.isDefault == 1) {
            var selloutDetails =
                await selloutProvider.getSelloutDetails(option1.id!, 2);
            if (filter &&
                (selloutDetails?.isSellout != 1 ||
                    selloutDetails?.surplus != 0.0)) {
              all.add(option1);
            } else if (!filter) {
              all.add(option1);
            }
          }
        } else {
          if (option1.options.isEmpty) continue;
          for (var op in option1.options) {
            if (op.isDefault == 1) {
              all.add(op);
            }
          }
        }
      }
    }

    return all;
  }
}
